﻿using System;
using System.Reflection;

[assembly:CLSCompliant(true)]

namespace DataGeneration.Domain.DataAccessLayer
{
    /// <summary>
    /// Typehelper
    /// </summary>
    [CLSCompliant(true)]
    public class Typehelper
    {
        /// <summary>
        /// Converts an object to the specified type.
        /// Supports anything that can be cast either from the source type or from a string,
        /// with extra support for Nullable types.
        /// </summary>
        /// <typeparam name="T">The target type to convert the value to.</typeparam>
        /// <param name="value">The value to convert.</param>
        /// <returns>The converted value.</returns>
        public static T EnsureType<T>(object value)
        {
            if (value == null || Convert.IsDBNull(value))
                return default(T);

            if (typeof(T) == typeof(string))
            {
                return (T)(object)value.ToString().Trim();
            }

            if (typeof(T) == typeof(Guid))
            {
                if (value is Guid) return (T)value;
                if (value is string) return (T)(object)new Guid((string)value);
                if (value is byte[]) return (T)(object)new Guid((byte[])value);

                return default(T);
            }

            if (value is T) return (T)value;

            if (typeof(bool) == typeof(T))
            {
                string s = value.ToString();
                if ("0" == s || "1" == s)
                    return (T)Convert.ChangeType("1" == s, typeof(T));
            }

            if (typeof(T).IsAssignableFrom(value.GetType()))
            {
                return (T)value;
            }

            try { return (T)Convert.ChangeType(value, typeof(T)); }
            catch (Exception)
            { /* swallow */ }

            try { return (T)Convert.ChangeType(value.ToString(), typeof(T)); }
            catch (Exception)
            { /* swallow */ }

            if (typeof(T).IsGenericType)
            {
                // get the generic argument... i.e. TypeName in Nullable<TypeName>
                Type genericType = typeof(T).GetGenericArguments()[0];

                return (T)InvokeStaticGenericMethod(typeof(Typehelper), "EnsureType", genericType, value);
            }
            return default(T);
        }

        /// <summary>
        /// Invokes a static generic method.
        /// </summary>
        /// <param name="target">The target.</param>
        /// <param name="methodName">Name of the method.</param>
        /// <param name="generic">The generic type.</param>
        /// <param name="args">The args.</param>
        /// <returns></returns>
        private static object InvokeStaticGenericMethod(Type target, string methodName, Type generic, params object[] args)
        {
            MethodInfo method = target.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
            MethodInfo typedMethod = method.MakeGenericMethod(generic);

            return typedMethod.Invoke(null, args);
        }


        /// <summary>
        /// Casts to type.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        public static object CastToType(object value,Type type)
        {
            string typeName = type.Name; 
            object returnValue;

            switch (typeName.Trim().ToLower())
            {
                case "byte[]":
                    returnValue = value;
                    break;
                case "datetime":
                    returnValue = Convert.ToDateTime(value);
                    break;
                case "int":
                case "int32":
                case "long":
                    returnValue = Convert.ToInt32(value);
                    break; 
                case "double":
                case "decimal":
                    returnValue = Convert.ToDouble(value);
                    break;
                default:
                    returnValue = Convert.ToString(value);
                    break;
            }

            return returnValue;
        }

    }
}


